//! Cache storage implementation

use super::TestCache;
use crate::error::{Result, Error, CacheError};
use crate::models::Value;
use async_trait::async_trait;
use redis::{Client, AsyncCommands};
use redis::aio::ConnectionManager;
use std::env;

/// In-memory cache implementation
#[derive(Debug)]
pub struct MemoryCache {
    // TODO: Add actual cache implementation
}

impl MemoryCache {
    pub fn new() -> Self {
        Self {}
    }
}

#[async_trait]
impl TestCache for MemoryCache {
    async fn set(&self, key: &str, value: &Value, ttl: Option<u64>) -> Result<()> {
        // TODO: Implement cache set
        Ok(())
    }
    
    async fn get(&self, key: &str) -> Result<Option<Value>> {
        // TODO: Implement cache get
        Ok(None)
    }
    
    async fn delete(&self, key: &str) -> Result<bool> {
        // TODO: Implement cache delete
        Ok(false)
    }
    
    async fn exists(&self, key: &str) -> Result<bool> {
        // TODO: Implement cache exists
        Ok(false)
    }
    
    async fn expire(&self, key: &str, ttl: u64) -> Result<bool> {
        // TODO: Implement cache expire
        Ok(false)
    }
    
    async fn clear(&self) -> Result<()> {
        // TODO: Implement cache clear
        Ok(())
    }
}

/// Redis cache implementation
pub struct RedisCache {
    manager: ConnectionManager,
}

impl std::fmt::Debug for RedisCache {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("RedisCache")
            .field("manager", &"<ConnectionManager>")
            .finish()
    }
}

impl Clone for RedisCache {
    fn clone(&self) -> Self {
        Self {
            manager: self.manager.clone(),
        }
    }
}

impl RedisCache {
    /// Create a new Redis cache instance
    pub async fn new(redis_url: &str) -> Result<Self> {
        let client = Client::open(redis_url)
            .map_err(|e| Error::Cache(CacheError::ConnectionFailed {
                message: format!("Failed to create Redis client: {}", e),
            }))?;

        let manager = ConnectionManager::new(client).await
            .map_err(|e| Error::Cache(CacheError::ConnectionFailed {
                message: format!("Failed to create Redis connection manager: {}", e),
            }))?;

        Ok(Self { manager })
    }

    /// Create a new instance from environment variables
    pub async fn from_env() -> Result<Self> {
        let redis_url = env::var("REDIS_URL")
            .unwrap_or_else(|_| "redis://localhost:6379".to_string());
        Self::new(&redis_url).await
    }

    /// Get a connection from the manager
    async fn get_connection(&self) -> Result<ConnectionManager> {
        Ok(self.manager.clone())
    }
}

#[async_trait]
impl TestCache for RedisCache {
    async fn set(&self, key: &str, value: &Value, ttl: Option<u64>) -> Result<()> {
        let mut conn = self.get_connection().await?;
        let serialized = serde_json::to_string(value)
            .map_err(|e| Error::Cache(CacheError::SerializationFailed {
                message: format!("Failed to serialize value: {}", e),
            }))?;

        if let Some(ttl_seconds) = ttl {
            conn.set_ex::<_, _, ()>(key, serialized, ttl_seconds).await
        } else {
            conn.set::<_, _, ()>(key, serialized).await
        }
        .map_err(|e| Error::Cache(CacheError::OperationFailed {
            operation: "set".to_string(),
            message: format!("Redis set failed: {}", e),
        }))?;

        Ok(())
    }

    async fn get(&self, key: &str) -> Result<Option<Value>> {
        let mut conn = self.get_connection().await?;
        let result: Option<String> = conn.get(key).await
            .map_err(|e| Error::Cache(CacheError::OperationFailed {
                operation: "get".to_string(),
                message: format!("Redis get failed: {}", e),
            }))?;

        match result {
            Some(serialized) => {
                let value = serde_json::from_str(&serialized)
                    .map_err(|e| Error::Cache(CacheError::DeserializationFailed {
                        message: format!("Failed to deserialize value: {}", e),
                    }))?;
                Ok(Some(value))
            }
            None => Ok(None),
        }
    }

    async fn delete(&self, key: &str) -> Result<bool> {
        let mut conn = self.get_connection().await?;
        let deleted: i32 = conn.del(key).await
            .map_err(|e| Error::Cache(CacheError::OperationFailed {
                operation: "delete".to_string(),
                message: format!("Redis delete failed: {}", e),
            }))?;

        Ok(deleted > 0)
    }

    async fn exists(&self, key: &str) -> Result<bool> {
        let mut conn = self.get_connection().await?;
        let exists: bool = conn.exists(key).await
            .map_err(|e| Error::Cache(CacheError::OperationFailed {
                operation: "exists".to_string(),
                message: format!("Redis exists failed: {}", e),
            }))?;

        Ok(exists)
    }

    async fn expire(&self, key: &str, ttl: u64) -> Result<bool> {
        let mut conn = self.get_connection().await?;
        let result: bool = conn.expire(key, ttl as i64).await
            .map_err(|e| Error::Cache(CacheError::OperationFailed {
                operation: "expire".to_string(),
                message: format!("Redis expire failed: {}", e),
            }))?;

        Ok(result)
    }

    async fn clear(&self) -> Result<()> {
        let mut conn = self.get_connection().await?;
        redis::cmd("FLUSHDB").query_async::<_, ()>(&mut conn).await
            .map_err(|e| Error::Cache(CacheError::OperationFailed {
                operation: "clear".to_string(),
                message: format!("Redis clear failed: {}", e),
            }))?;

        Ok(())
    }
}
